﻿using System;
using game.character.bullet;
using game.character.weapon;
using lumo;
using lumo.display.animation2D;
using lumo.display.object2D;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Dynamics;
using System.Collections.Generic;

namespace game.character.enemy
{
    /// <summary>
    /// Base for enemy.
    /// </summary>
    public abstract class EnemyBase : GameEvent, IShootable
    {
        public BulletManager BulletManager { get; protected set; }
        public Weapon Weapon { get; protected set; }


        public EnemyBase(LumoComponent component, GameMap map, Tiled.Object obj)
            : base(component, map, obj)
        {
            BulletManager = new BulletManager();
            ScreenHighPriority = true;
        }

        protected override abstract void CreateBody();
        protected override abstract void SetupParameters();
        protected override abstract void SetupAI();
        protected virtual void SetupDead()
        {
            // Get Properties.
            this.Texture = _LumoComponent.Content.Load<Texture2D>("Graphics/Enemies/Normal/Fish 2");
            // Animation.
            FrameAnimation2D Animation = new FrameAnimation2D(3, 1, new uint[] { 0, 1, 2, 1, 2, 0, 2, 1 }, 800.0f, Smooth.None, Loop.Reset, this);
            AIOnUpdate(
                () =>
                {
                    Animation.Update(_LumoComponent);
                    return null;
                }
            );
            // Clear the velocity.
            AIClearVelocity();

            Vector2 Offset = Vector2.Zero;
            while (Offset.Length() <= 4000)
                Offset = new Vector2(Global.Random.Next(-3000, 3000), Global.Random.Next(-3000, 3000));
            Vector2 Dest = this.Position + Offset;
            AIMoveTo(Dest, 400.0f, 0);
            AIOnCondition(
                () => (this.Position - Dest).Length() <= 100.0f,
                () =>
                {
                    this.Deleted = true;
                    return null;
                }
            );
            // Turn on AI
            AITurn();
        }

        /// <summary>
        /// Update the enemy.
        /// </summary>
        /// <param name="component">THe component to update the Enemy on</param>
        protected float GetFloatProperty(string Name, float Default = 0.0f )
        {
            // Update the base.
            if (!_TiledObject.Properties.ContainsKey(Name))
                return Default;
            float Value;
            if (!float.TryParse(_TiledObject.Properties[Name], out Value))
                return Default;
            return Value;
        }

        /// <summary>
        /// Update the enemy.
        /// </summary>
        /// <param name="component">THe component to update the Enemy on</param>
        public override void Update(LumoComponent component)
        {
            // Update the base.
            base.Update(component);
            if (Battler != null)
            {
                if (Battler.HP <= 0)
                {
                    Battler = null;
                    _AIActionList.Clear();
                    SetupDead();
                }
            }
            BulletManager.Update(component);
        }

        /// <summary>
        /// Enumerator for Range AI pattern
        /// </summary>
        public enum AIRangePattern
        {
            /// <summary>None</summary>
            None = 0,
            /// <summary>Follow when on range</summary>
            Follow = 0x01,
            /// <summary>Flee when on range</summary>
            Flee = 0x02,
            /// <summary>Lock when on range</summary>
            Lock = 0x04,
            /// <summary>All range patterns</summary>
            All = Follow | Flee | Lock
        }

        /// <summary>
        /// AI Command to set when character is on range.
        /// </summary>
        /// <param name="Range">The range to do the action</param>
        /// <param name="Other">The other character to setup</param>
        /// <param name="Speed">The speed of the movement</param>
        /// <param name="Pattern">The pattern</param>
        /// <param name="OnRangeGroup">THe group of the action when on range</param>
        /// <param name="OnNotRangeGroup">The group of the action when not on range</param>
        /// <returns>The AI Action</returns>
        protected AIAction AIOnRangePattern(float Range, GameCharacter Other, float Speed, AIRangePattern Pattern, string OnRangeGroup = null, string OnNotRangeGroup = null)
        {
            if ((Pattern & AIRangePattern.All) == AIRangePattern.None)
                return null;

            return AIOnRange(
                Range,
                Other,
                () =>
                {
                    Vector2 Difference = Other.Position - this.Position;
                    if ((Pattern & AIRangePattern.Follow) != 0)
                    {
                        if (Difference.Length() >= 4)
                        {
                            Vector2 Direction = Vector2.Normalize(Difference);
                            Body.LinearVelocity += Vector2.Multiply(Direction, Speed / Global.PhysicsScale);
                        }
                    }
                    if ((Pattern & AIRangePattern.Flee) != 0)
                    {
                        if (Other.Position != this.Position)
                        {
                            Vector2 Direction = Vector2.Normalize(Difference);
                            Body.LinearVelocity -= Vector2.Multiply(Direction, Speed / Global.PhysicsScale);
                        }
                    }
                    if ((Pattern & AIRangePattern.Lock) != 0)
                    {
                        float arc = (float)Math.Atan2(Difference.Y, Difference.X);
                        Body.Rotation = arc - (float)Math.PI;
                    }
                    return OnRangeGroup;
                },
                () =>
                {
                    return OnNotRangeGroup;
                }
            );
        }

        /// <summary>
        /// Fixed moviment
        /// </summary>
        /// <param name="Points"></param>
        /// <param name="Speed"></param>
        /// <param name="Group"></param>
        /// <returns></returns>
        protected AIAction AIClearVelocity()
        {
            return AIOnUpdate(
                () =>
                {
                    Body.AngularVelocity = 0;
                    Body.LinearVelocity = Vector2.Zero;
                    return null;
                }
            );
        }

        /// <summary>
        /// Fixed moviment
        /// </summary>
        /// <param name="Points"></param>
        /// <param name="Speed"></param>
        /// <param name="Group"></param>
        /// <returns></returns>
        protected AIAction AIFixedMoviment(List<Vector2> Points, float Speed, float Distance, string Group = null)
        {
            if (Points.Count <= 1)
                return null;
            int[] Target = new int[] { 1 };
            this.Position = Points[0];
            return AIOnUpdate(
                () =>
                {
                    Vector2 Dest = Points[Target[0]];
                    Vector2 Difference = Dest - this.Position;
                    if (Difference.Length() <= Distance)
                    {
                        Target[0] = (Target[0] + 1) % Points.Count;
                    }
                    else
                    {
                        Vector2 Direction = Vector2.Normalize(Difference);
                        Body.LinearVelocity += (Vector2.Multiply(Direction, Speed / Global.PhysicsScale));
                    }
                    return Group;
                }
            );
        }

        /// <summary>
        /// Fixed moviment
        /// </summary>
        /// <param name="Points"></param>
        /// <param name="Speed"></param>
        /// <param name="Group"></param>
        /// <returns></returns>
        protected AIAction AIMoveTo(Vector2 Dest, float Speed, float Distance, string Group = null)
        {
            bool[] Moved = new bool[] { false };
            return AIOnUpdate(
                () =>
                {
                    if (!Moved[0])
                    {
                        Vector2 Difference = Dest - this.Position;
                        if (Difference.Length() <= Distance)
                        {
                            Moved[0] = true;
                        }
                        else
                        {
                            Vector2 Direction = Vector2.Normalize(Difference);
                            Body.LinearVelocity += (Vector2.Multiply(Direction, Speed / Global.PhysicsScale));
                        }
                    }
                    return Group;
                }
            );
        }

        /// <summary>
        /// Turn condition.
        /// </summary>
        /// <param name="Group"></param>
        /// <param name="NotGroup"></param>
        /// <returns></returns>
        protected AIAction AITurn(string Group = null, string NotGroup = null)
        {
            // T
            return AIOnCondition(
                () => Body.LinearVelocity.X >= 0,
                () =>
                {
                    ScreenMirrorX = false;
                    return Group;
                },
                () =>
                {
                    ScreenMirrorX = true;
                    return NotGroup;
                }
            );
        }
        /// <summary>
        /// Damage on contact
        /// </summary>
        /// <param name="Other"></param>
        /// <param name="Group"></param>
        /// <returns></returns>
        protected AIAction AIDamageOnContact(GameCharacter Other, string Group = null)
        {
            // On Contact
            return AIOnContact(
                Other,
                () =>
                {
                    this.Battler.ContinuousHit(Other.Battler, (float)_LumoComponent.GameTime.ElapsedGameTime.TotalMilliseconds);
                    return Group;
                }
            );
        }

    }

}